/**
 * 
 */
package com.vivid.dynamics.junit.impl;

import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

import com.vivid.dynamics.junit.JUnitTemplate;

/**
 * A special JUnit template supporting Spring and Hibernate functionalities.
 * 
 * @author jason
 * created at:: Jul 28, 2011 2:27:18 PM
 */
/*
 * use the JUnit class runner provided by Spring
 */
@RunWith(value = SpringJUnit4ClassRunner.class)
/*
 * load the context files, locations could be an array of config files
 */
@ContextConfiguration(locations={"classpath:/config/spring-config.xml"})
/*
 * this is a MUST listener... since we need to support @Autowired bean(s)
 */
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class SpringHibernateSupportJUnitTemplate extends JUnitTemplate {
	/**
	 * ApplicationContext; refer to the config locations stated by 
	 * &#64;ContextConfiguration
	 */
    protected @Autowired ApplicationContext applicationContext;
    /**
     * TransactionManager; refer to the configured applicationContext above
     * for the available PlatformTransactionManager instance(s)
     */
	protected @Autowired PlatformTransactionManager txManager;
	/**
	 * the transaction-status created by the above txManager. Mainly use it 
	 * to set the "rollback" flag
	 */
	protected TransactionStatus tx;
	/**
	 * should the txManager rollback/undone all operations at the db?
	 */
	protected boolean rollbackAfterACtion=true;
	
	/* ****************** */
	/* *	ctors		* */
	/* ****************** */
	
	/**
	 * ctor.
	 */
	public SpringHibernateSupportJUnitTemplate() {
		super(SpringHibernateSupportJUnitTemplate.class);
	}
	/**
	 * ctor with the Class parameter; creating the Logger with the given 
	 * class's setup.
	 * @param pClass
	 */
	public SpringHibernateSupportJUnitTemplate(@SuppressWarnings("rawtypes") Class pClass) {
		this(pClass, true);
	}
	/**
	 * ctor with Class parameter for setting up the Logger; pRollbackAfterAction to determine
	 * whether rollback should be done after the JUnit method. Default is true
	 * @param pClass
	 * @param pRollbackAfterAction
	 */
	public SpringHibernateSupportJUnitTemplate(@SuppressWarnings("rawtypes") Class pClass, boolean pRollbackAfterAction) {
		super(pClass);
		this.rollbackAfterACtion=pRollbackAfterAction;
	}
	
	/* ****************************************** */
	/* *	JUnit related lifecycle methods		* */
	/* ****************************************** */
	
	/**
	 * do before any Test. Since Hibernate requires a live Transaction/Session 
	 * if the associations are lazily loaded; therefore create a Transaction out;
	 * underneath the Transaction, a Session is created at the back 
	 * (refer to the spring configuration...); 
	 * and it is accessible by calling <b>sessionFactory.getCurrentSession()</b>
	 * 
	 * @author jason
	 * created at:: Jun 7, 2011  2:22:37 PM
	 */
	@Before
	@Override
	public void doBeforeAction() {
		super.doBeforeAction();
		
		if (this.txManager!=null) {
			DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED);
			transactionAttribute.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
			
			this.tx=this.txManager.getTransaction(transactionAttribute);
		}
	}

	/**
	 * do after any Test. Depends what you want; default case is to 
	 * <b>rollback</b> any done operations to the db.
	 * 
	 * @author jason
	 * created at:: Jun 7, 2011  2:26:21 PM
	 */
	@After
	@Override
	public void doAfterAction() {
		// default is... rollback all operations (similar to spring's jdbcTemplate test)
		if (this.txManager!=null && this.tx!=null) {
			if (this.rollbackAfterACtion) {
				this.tx.setRollbackOnly();
				/*
				 * sort of bug or logical constraint; 
				 * you SHOULD NOT call rollback() if you have setted the rollback flag to "true"
				 * PS. for most case, setRollbackOnly=true WOULD be enough~
				 */
				//this.txManager.rollback(this.tx);
				
			} else {
				// unless the tx is already set to false somewhere else, a commit will be done
				this.txManager.commit(this.tx);
			}
		}
		super.doAfterAction();
	}
}
